Skip to content

fix(sparse): floor() returns a copy instead of mutating its input (#740)#894

Merged
kennethshsu merged 2 commits into
casact:mainfrom
SaguaroDev:740-sparse-floor-copy
Jun 1, 2026
Merged

fix(sparse): floor() returns a copy instead of mutating its input (#740)#894
kennethshsu merged 2 commits into
casact:mainfrom
SaguaroDev:740-sparse-floor-copy

Conversation

@SaguaroDev
Copy link
Copy Markdown
Contributor

@SaguaroDev SaguaroDev commented May 31, 2026

Closes #740.

chainladder.utils.sparse.floor(x) reassigned x.data in place, mutating the caller's COO array as a side effect:

def floor(x):
    x.data = np.floor(x.data)
    return x

Since np.floor() returns a copy, this makes floor() match that contract by copying before flooring, using the same .copy() idiom already present in nan_to_num() in the same module:

def floor(x):
    x = x.copy()
    x.data = np.floor(x.data)
    return x

Behavior now matches the issue's expectation:

a = array([1.2, 2.7, -0.3])
floor(a)
a.todense()        # array([ 1.2,  2.7, -0.3])  (unchanged)

a = floor(a)
a.todense()        # array([ 1.,  2., -1.])

The only internal caller (chainladder/core/correlation.py) already consumes the return value (m = xp.floor((n - 1) / 2)), so the change is behavior-preserving there.

Updated test_floor_mutates_in_place (which asserted result is a) to test_floor_returns_copy, asserting the result is a new object and the input is left unmodified. test_sparse.py and test_correlation.py pass (12 passed).


Note

Low Risk
Localized utility fix; the only in-repo caller already uses the return value, so behavior there is unchanged aside from avoiding accidental input mutation.

Overview
chainladder.utils.sparse.floor no longer mutates the input COO array. It now **copy()**s first (same pattern as nan_to_num in that module), then floors x.data, aligning with np.floor’s copy semantics (issue #740). Type hints COOCOO were added on floor.

The test that expected result is a was renamed to test_floor_returns_copy and now checks the input values stay unchanged while the returned array is floored.

Reviewed by Cursor Bugbot for commit 876211f. Bugbot is set up for automated code reviews on this repo. Configure here.

…sact#740)

chainladder.utils.sparse.floor(x) reassigned x.data in place, mutating
the caller's COO array. np.floor() returns a copy, so floor() should too.

Copy the array before flooring its data, mirroring the existing
nan_to_num() idiom in the same module. The one internal caller
(core/correlation.py) already uses the return value, so behavior is
preserved.

Updated the test that asserted in-place mutation to assert the new copy
semantics: the result is a new object and the input is left unchanged.

Closes casact#740.
@codecov
Copy link
Copy Markdown

codecov Bot commented May 31, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 86.90%. Comparing base (8a9a46e) to head (876211f).

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #894   +/-   ##
=======================================
  Coverage   86.90%   86.90%           
=======================================
  Files          87       87           
  Lines        4932     4933    +1     
  Branches      624      624           
=======================================
+ Hits         4286     4287    +1     
  Misses        456      456           
  Partials      190      190           
Flag Coverage Δ
unittests 86.90% <100.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@henrydingliu
Copy link
Copy Markdown
Collaborator

since we are already touching this function, may I please trouble you to add type hinting for the input and output?

Per @henrydingliu's review on casact#894: annotate floor(x: COO) -> COO.
COO is already imported at the module top (from sparse import COO as COO),
so no new import is needed.
@SaguaroDev
Copy link
Copy Markdown
Contributor Author

Added in 876211ff: def floor(x: COO) -> COO:. COO is already imported at the top of sparse.py (from sparse import COO as COO), so no new import. test_sparse + test_correlation still pass (12).

@kennethshsu kennethshsu merged commit ef837b0 into casact:main Jun 1, 2026
10 checks passed
@SaguaroDev SaguaroDev deleted the 740-sparse-floor-copy branch June 3, 2026 14:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[ENH] Update chainladder.utils.sparse.floor(x) to return a copy of x

3 participants